#include "H264SPS.hpp"
#include "Bistream.hpp"


void scaling_list(uint32_t* scaling_list, int sizeOfScalingList, uint32_t useDefalutScalingMatrixFlag,BitStream& bitstrean){
    int lastScale = 8;
    int nextScale = 8;
    for(int i =0; i < sizeOfScalingList;i++){
        if(nextScale != 0){
            int delta_scale = bitstream.ReadSE();
            nextScale = (lastScale + delta_scale + 256) %256;
            useDefalutScalingMatrixFlag = ( i ==0 && nextScale == 0)
        }
        scaling_list[i] = (nextScale == 0)? lastScale : nextScale;
        lastScale = scaling_list[i];
    }
    return 0;
}





int NaluSPS::GetWidthHeight(){
    width = (pic_width_in_mbs_minus1+1)*16;
    Height = (pic_height_in_map_units_minus1+1)*16;

    if(frame_cropping_flag){
        int crop_unit_x = 0;
        int crop_unit_y = 0;
        if(ChromaArrayType == 0){
            crop_unit_x = 1;
            crop_unit_y = 2 - frame_mbs_only_flag;
        }
        else if(ChromaArrayType == 1 || ChromaArrayType == 2 || ChromaArrayType ==3){
            crop_unit_x = SubWidthC;
            crop_unit_y = subHeightC;
        }

        width -= crop_unit_x*(frame_cropping_left_offset + frame_cropping_right_offset);
        Height -= crop_unit_y*(frame_cropping_top_offset + frame_cropping_bottom_offset);

    }
    return 0;

}

int NaluSPS::Parse(){
    uint8_t* buf = rbsp.RBSPBuf;
    int buflen = rbsp.RBSPLen;

    BitStream bitstream(buf+1,bufLen-1);
    this->profile_idc = bitstream.ReadU(8);
    this->constraint_set0_flag = bitstream.ReadU1();
    this->constraint_set1_flag = bitstream.ReadU1();
    this->constraint_set2_flag = bitstream.ReadU1();
    this->constraint_set3_flag = bitstream.ReadU1();
    this->constraint_set4_flag = bitstream.ReadU1();
    this->constraint_set5_flag = bitstream.ReadU1();
    this->reserved_zero_2bit  = bitstream.ReadU(4);
    this->level_idc = bitstream.ReadU(8);
    this->seq_parameter_set_id = bitstream.ReadUE();
    if(profile_idc == 100 || profile_idc == 110 || profile_idc==122 ||profile_idc == 244||profile_idc==44||profile_idc==83||
       profile_idc ==86 ||profile_idc == 118||profile_idc == 128){
        this->chroma_format_idc = bitstream.ReadUE();
        if(this->chroma_format_idc == 3)
            separate_colour_plane_flag = bitstream.ReadU1();
        else
        this->bit_depth_luma_minus8 = bitstream.ReadUE();
        this->bit_depth_chroma_minus8 = bitstream.ReadUE();
        this->qpprime_y_zero_transform_bypass_flag = bitstream.ReadU1();
        this->seq_scaling_matrix_present_flag = bitstream.ReadU1();
        if(this->seq_scaling_list_present_flag){
            for(int i = 0; i < ((chroma_format_idc !=3 )? 8:12 ); i++){
                seq_scaling_list_present_flag[i] = bitstream.ReadU1();
                if(seq_scaling_list_present_flag[i]){
                    if(i < 6){
                        scaling_list(ScalingList4x4[i],16, UseDefaultScalingMatrix4x4Flag[i], bitstream);
                    }
                    else{
                        scaling_list(ScalingList8x8[i-6], 64, UseDefaultScalingMatrix8x8Flag[i-6]);
                    }
                }
            }

        }

    }
    else{
        chroma_format_idc = 1;
    }
    
    if(seq_parameter_set_id == 0){
        ChromaArrayType = chroma_format_idc;
    }
    else{
        ChromaArrayType = 0;
    }
    switch (ChromaArrayType)
    {
    case 1:
        subHeightC = 2;
        SubWidthC = 2;
        break;
    case 2:
        subHeightC = 1;
        SubWidthC = 2;
        break;
    case 3:
        subHeightC = 1;
        SubWidthC = 1;
    default:
        break;
    }

    this->log2_max_frame_num_minus4 = bitstream.ReadUE();
    
    this->pic_order_cnt_type = bitstream.ReadUE();
    if(this->pic_order_cnt_type == 0)
        this->log2_max_pic_order_cnt_lsb_minus4 = bitstream.ReadUE();
    else if(this->pic_order_cnt_type == 1){
        this->delta_pic_order_always_zero_flag = bitstream.ReadU1();
        this->offset_for_non_ref_pic = bitstream.ReadSE();
        this->offset_for_top_to_bottom_field = bitstream.ReadSE();
        this->num_ref_frames_in_pic_order_cnt_cycle = bitstream.ReadUE();
        for(int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++){
            this->offset_for_ref_frame[i] = bitstream.ReadSE();
        }
    }

    this->max_num_ref_frames = bitstream.ReadUE();
    this->gaps_in_frame_num_value_allowed_flag = bitstream.ReadU1();
    this->pic_width_in_mbs_minus1 = bitstream.ReadUE();
    this->pic_height_in_map_units_minus1 = bitstream.ReadUE();
    this->frame_mbs_only_flag = bitstream.ReadU1();
    if(!this->frame_mbs_only_flag){
        this->mb_adaptive_frame_field_flag = bitstream.ReadU1();
    }

    this->direct_8x8_inference_flag = bitstream.ReadU1();
    this->frame_cropping_flag = bitstream.ReadU1();
    if(this->frame_cropping_flag){
        this->frame_cropping_left_offset = bitstream.ReadUE();
        this->frame_cropping_right_offset = bitstream.ReadUE();
        this->frame_cropping_top_offset = bitstream.ReadUE();
        this->frame_cropping_bottom_offset = bitstream.ReadUE();
    }
    this->vui_parameters_present_flag = bitstream.ReadU1();

    return 0;
}

